
#include "BfcpUdpTransport.hxx"
#include "BfcpMessage.hxx"
#include "BfcpSendData.hxx"
#include "bfcp_message_processing/bfcp_strings.h"

#include "zklog/zklog_wrapper.h"

using namespace resip;

namespace bfcp
{

    BfcpUdpTransport::BfcpUdpTransport(const resip::Data& intfc,
                                       int portNum,
                                       resip::IpVersion version,
                                       resip::Fifo<BfcpTransactionMessage>& rxFifo)
        : BfcpTransportIf(intfc, portNum, version, rxFifo)
        , mInWritable(false)
        , mRxBuffer(new char[BFCP_MAX_ALLOWED_SIZE + 1], BFCP_MAX_ALLOWED_SIZE)
    {
        mTuple.setType(UDP);
        mFd = BfcpTransportIf::socket(transport(), version);
        mTuple.mFlowKey = (FlowKey)mFd;
        bind();
        makeSocketNonBlocking(mFd);// also makes it non-blocking

        ZKLOG_INFO << "Creating UDP transport host=" << intfc
            << " port=" << mTuple.getPort()
            << " ipv4=" << bool(version == V4);

        mTxFifo.setDescription("BfcpUdpTransport::mTxFifo");
    }

    BfcpUdpTransport::~BfcpUdpTransport()
    {
        ZKLOG_INFO << "Shutting down " << mTuple
            << " evt=" << (mPollGrp ? 1 : 0);

        if (mRxBuffer.first)
        {
            delete[] mRxBuffer.first;
            mRxBuffer.first = NULL;
            mRxBuffer.second = 0;
        }

        setPollGrp(0);
    }

    void BfcpUdpTransport::setRcvBufLen(int buflen)
    {
        setSocketRcvBufLen(mFd, buflen);
    }

    void BfcpUdpTransport::setPollGrp(resip::FdPollGrp *grp)
    {
        if (mPollGrp)
        {
            mPollGrp->delPollItem(mPollItemHandle);
            mPollItemHandle = 0;
        }

        if (mFd != INVALID_SOCKET && grp)
        {
            mPollItemHandle = grp->addPollItem(mFd, FPEM_Read, this);
            // above released by InternalTransport destructor
            // ?bwc? Is this really a good idea? If the InternalTransport d'tor is
            // freeing this, shouldn't InternalTransport::setPollGrp() handle 
            // creating it?
        }

        BfcpTransportIf::setPollGrp(grp);
    }

    void BfcpUdpTransport::processPollEvent(resip::FdPollEventMask mask)
    {
        if (mask & FPEM_Error)
        {
            resip_assert(0);
        }
        if (mask & FPEM_Write)
        {
            processTxAll();
            updateEvents();   // turn-off writability
        }
        if (mask & FPEM_Read)
        {
            processRxAll();
        }
    }

    void BfcpUdpTransport::process()
    {
        mStateMachineFifo.flush();

        processTxAll();

        if (mPollGrp)
        {
            updateEvents();
        }
    }

    void BfcpUdpTransport::processRxAll()
    {
        for (;;)
        {
            BfcpNetTuple sender(mTuple);
            int len = processRxRecv(mRxBuffer, sender);
            if (len <= 0)
            {
                break;
            }

            if (processRxParse(mRxBuffer, len, sender))
            {
                
            }
        }
    }

    int BfcpUdpTransport::processRxRecv(BufferPair& bufferPair, BfcpNetTuple& sender)
    {
        resip_assert(bufferPair.first);

        for (;;)
        {
            // !jf! how do we tell if it discarded bytes
            // !ah! we use the len-1 trick :-(
            socklen_t slen = sender.length();
            int len = recvfrom(mFd,
                bufferPair.first,
                BFCP_MAX_ALLOWED_SIZE,
                0 /*flags */,
                &sender.getMutableSockaddr(),
                &slen);
            if (len == SOCKET_ERROR)
            {
                int err = getErrno();
                if (err != EAGAIN && err != EWOULDBLOCK) // Treat EGAIN and EWOULDBLOCK as the same: http://stackoverflow.com/questions/7003234/which-systems-define-eagain-and-ewouldblock-as-different-values
                {
                    error(err);
                }
                len = 0;
            }
            if (len > BFCP_MAX_ALLOWED_SIZE)
            {
                ZKLOG_INFO << "BFCP Datagram exceeded max length: " << BFCP_MAX_ALLOWED_SIZE;
                continue;
            }
            bufferPair.first[len] = 0; // null terminate the buffer string just to make debug easier and reduce errors
            bufferPair.second = len;
            return len;
        }
    }

    bool BfcpUdpTransport::processRxParse(BufferPair& bufferPair, int len, BfcpNetTuple& sender)
    {
        //ZKLOG_TRACE << "UDP Rcv : " << len << " b";
        //ZKLOG_TRACE << Data(bufferPair.first, len).escaped().c_str();

        // common header(12 bytes)
        if (bufferPair.second < 12)
        {
            ZKLOG_ERROR << "Invalid bfcp packet length - common header require 12 bytes!";
            return false;
        }
 
        //ZKLOG_TRACE << "Received from: " << sender << ", received on:" << mTuple;
        BfcpMessage* message = new BfcpMessage((unsigned char*)bufferPair.first, bufferPair.second, sender, mTuple);

        if (message->isInvalid())
        {
            delete message;
            ZKLOG_ERROR << "bfcp raw packet parse error!";
            return false;
        }

        pushRxMsgUp(message);

        return true;
    }

    void BfcpUdpTransport::processTxAll()
    {
        BfcpSendData *msg;
        while ((msg = mTxFifoOutBuffer.getNext(RESIP_FIFO_NOWAIT)) != NULL)
        {
            processTxOne(msg);
        }
    }

    void BfcpUdpTransport::processTxOne(BfcpSendData *data)
    {
        resip_assert(data);

        std::auto_ptr<BfcpSendData> sendData(data);

        if (data->mCommand != BfcpSendData::NoCommand)
        {
            // We don't handle any special SendData commands in the UDP transport yet.
            return;
        }

        //ZKLOG_TRACE << "Sent: " <<  sendData->data;
        //ZKLOG_TRACE << "Sending message on udp.";
        resip_assert(sendData->mDestination.getPort() != 0);

        const sockaddr& addr = sendData->mDestination.getSockaddr();
        int expected;
        int count;

        {
            expected = (int)sendData->mData->length;
            count = sendto(mFd,
                (const char*)sendData->mData->buffer, (int)sendData->mData->length,
                0, // flags
                &addr, (int)sendData->mDestination.length());
        }

        if (count == SOCKET_ERROR)
        {
            int e = getErrno();
            error(e);
            ZKLOG_ERROR << "Failed (" << e << ") sending to " << sendData->mDestination;
        }
        else
        {
            if (count != expected)
            {
                ZKLOG_ERROR << "BfcpUdpTransport - send buffer full";
            }
        }
    }

    void BfcpUdpTransport::updateEvents()
    {
        bool haveMsg = mTxFifoOutBuffer.messageAvailable();
        if (!mInWritable && haveMsg)
        {
            mPollGrp->modPollItem(mPollItemHandle, FPEM_Read | FPEM_Write);
            mInWritable = true;
        }
        else if (mInWritable && !haveMsg)
        {
            mPollGrp->modPollItem(mPollItemHandle, FPEM_Read);
            mInWritable = false;
        }
    }

}
